package service.util;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;

import service.Service;
import dao.util.DaoAnnotation;
import dao.util.DaoContextEnum;
import dao.util.DaoFactory;


public class ServiceFactory
{
	
	
	public static Service<?> getServiceForName(String serviceName, DaoContextEnum dao)
	{
		Class<?>[] classes;
		ClassLoader cld = ServiceFactory.class.getClassLoader();
		classes = getClasses(Service.class.getPackage().getName(), cld);
		for(Class<?> klasse:classes)
		{
			if(klasse.getAnnotation(ServiceAnnotation.class).serviceName().equals(serviceName))
			{
				return getConfiguredService(klasse, klasse.getAnnotation(ServiceAnnotation.class).daos(), dao);
				
			}
		}
		
		return null;
	}
	
	

	/**
	 * @param pckgname
	 * @param cld 
	 * @return
	 * @throws ClassNotFoundException
	 * @throws UnsupportedEncodingException
	 */
	@SuppressWarnings("rawtypes")
	private static Class[] getClasses(String pckgname, ClassLoader cld)
	{
		ArrayList<Class> classes = new ArrayList<Class>();
		// Get a File object for the package
		File directory = null;
		try 
		{
			if (cld == null) 
			{
				throw new ClassNotFoundException("Can't get class loader.");
			}
			String path = pckgname.replace('.', '/');
			URL resource = cld.getResource(path); 
			if (resource == null) 
			{
				throw new ClassNotFoundException("No resource for " + path);
			}
			directory = new File(URLDecoder.decode(resource.getPath(),"UTF-8"));
			
			
			if (directory.exists()) 
			{
				// Get the list of the files contained in the package
				String[] files = directory.list();
				
				for (int i = 0; i < files.length; i++) 
				{
					// we are only interested in .class files
					if (files[i].endsWith(".class")) 
					{
						Class<?> dao = Class.forName(pckgname + '.' + files[i].substring(0, files[i].length() - 6));
						if(dao.isAnnotationPresent(ServiceAnnotation.class))
						{
							classes.add(dao);
						}
					} 
					else if(!files[i].contains("."))
					{
						// recursive opening of subpackages
						
						Class[] klassen = getClasses(pckgname + "." + files[i], cld);
						for(Class klasse:klassen)
						{
							classes.add(klasse);
						}
					}
				}
			} 
			else 
			{
				throw new ServiceException(pckgname + " does not appear to be a valid package");
			}
			Class[] classesA = new Class[classes.size()];
			classes.toArray(classesA);
			return classesA;
		} 
			catch (NullPointerException x) 
			{
				throw new ServiceException(pckgname + " (" + directory + ") does not appear to be a valid package", x);
		} 
			catch (ClassNotFoundException e)
		{
			throw new ServiceException(e.getMessage(), e);
		} 
			catch (UnsupportedEncodingException e)
		{
			throw new ServiceException(e.getMessage(), e);
		}
	}
	
	private static Service<?> getConfiguredService(Class<?> clazz, String[] daos, DaoContextEnum daoContext)
	{
		try
		{
			Service<?> service = (Service<?>) clazz.newInstance();
			
			Class<?>[] daoClasses = DaoFactory.getDaoClasses();
			for(String dao:daos){
				for(Class<?> clazzz:daoClasses){
					DaoAnnotation annotation = clazzz.getAnnotation(DaoAnnotation.class);
					if(dao.equals(annotation.className()) && daoContext.equals(annotation.use())){
						Method method = clazz.getMethod("set" + dao, annotation.daoInterface());
						method.invoke(service, clazzz.newInstance());
					}
				}
			}
			
			return service;
		} catch (IllegalArgumentException e)
		{
			throw new ServiceException(e.getMessage(), e);
		} catch (IllegalAccessException e)
		{
			throw new ServiceException(e.getMessage(), e);
		} catch (InvocationTargetException e)
		{
			throw new ServiceException(e.getMessage(), e);
		} catch (InstantiationException e)
		{
			throw new ServiceException(e.getMessage(), e);
		} catch (SecurityException e)
		{
			throw new ServiceException(e.getMessage(), e);
		} catch (NoSuchMethodException e)
		{
			throw new ServiceException(e.getMessage(), e);
		} catch (UnsupportedEncodingException e)
		{
			throw new ServiceException(e.getMessage(), e);
		} catch (ClassNotFoundException e)
		{
			throw new ServiceException(e.getMessage(), e);
		}
	}
}
